# -*- coding: utf-8 -*-
"""
os_user_auth.py
跨平台系统账户认证（Linux 使用 PAM, Windows 使用 LogonUser）
无需外部依赖
"""

import ctypes
import platform
import getpass

IS_WINDOWS = platform.system().lower().startswith("win")

if IS_WINDOWS:
    # ----------------- Windows -----------------
    import ctypes.wintypes as wt
    advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)

    LOGON32_LOGON_INTERACTIVE = 2
    LOGON32_PROVIDER_DEFAULT = 0

    LogonUserW = advapi32.LogonUserW
    LogonUserW.argtypes = [
        wt.LPWSTR, wt.LPWSTR, wt.LPWSTR,
        wt.DWORD, wt.DWORD, ctypes.POINTER(wt.HANDLE)
    ]
    LogonUserW.restype = wt.BOOL

    def authenticate(username: str, password: str, domain: str = ".") -> bool:
        """Windows 用户验证"""
        handle = wt.HANDLE()
        ok = LogonUserW(
            username, domain, password,
            LOGON32_LOGON_INTERACTIVE,
            LOGON32_PROVIDER_DEFAULT,
            ctypes.byref(handle)
        )
        if ok:
            ctypes.windll.kernel32.CloseHandle(handle)
        return bool(ok)

else:
    # ----------------- Linux (PAM) -----------------
    libpam = ctypes.CDLL("libpam.so.0")
    PAM_SUCCESS = 0

    class PamHandle(ctypes.Structure):
        _fields_ = [("handle", ctypes.c_void_p)]

    class PamMessage(ctypes.Structure):
        _fields_ = [("msg_style", ctypes.c_int),
                    ("msg", ctypes.c_char_p)]

    class PamResponse(ctypes.Structure):
        _fields_ = [("resp", ctypes.c_char_p),
                    ("resp_retcode", ctypes.c_int)]

    # pam_conv 回调类型
    CONV_FUNC = ctypes.CFUNCTYPE(
        ctypes.c_int,
        ctypes.c_int,
        ctypes.POINTER(ctypes.POINTER(PamMessage)),
        ctypes.POINTER(ctypes.POINTER(PamResponse)),
        ctypes.c_void_p
    )

    # 回调函数
    @CONV_FUNC
    def _conv(n_messages, messages, p_response, appdata_ptr):
        password = ctypes.cast(appdata_ptr, ctypes.c_char_p).value
        # 分配 PamResponse 数组
        response = ctypes.cast(
            libpam.calloc(n_messages, ctypes.sizeof(PamResponse)),
            ctypes.POINTER(PamResponse)
        )
        response[0].resp = libpam.strdup(password)
        p_response[0] = response
        return PAM_SUCCESS

    class PamConv(ctypes.Structure):
        _fields_ = [("conv", CONV_FUNC),
                    ("appdata_ptr", ctypes.c_void_p)]

    pam_start = libpam.pam_start
    pam_authenticate = libpam.pam_authenticate
    pam_end = libpam.pam_end

    def authenticate(username: str, password: str) -> bool:
        """Linux PAM 用户验证"""
        pamh = PamHandle()
        # 将密码转换为c_char_p，然后转换为c_void_p
        password_c = ctypes.c_char_p(password.encode())
        conv = PamConv(_conv, ctypes.cast(password_c, ctypes.c_void_p))
        if pam_start(b"login", username.encode(), ctypes.byref(conv), ctypes.byref(pamh)) != PAM_SUCCESS:
            return False
        retval = pam_authenticate(pamh, 0)
        pam_end(pamh, retval)
        return retval == PAM_SUCCESS


if __name__ == "__main__":
    print(f"系统: {platform.system()}")
    user = input("用户名: ")
    pwd = getpass.getpass("密码: ")

    ok = authenticate(user, pwd)
    print("✅ 认证成功" if ok else "❌ 认证失败")
